package cn.tedu.eliminate;
import javax.swing.JFrame;
import javax.swing.JPanel;
import java.awt.Graphics;
import java.util.Random;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.Arrays;
/** 窗口类 */
public class World extends JPanel {
    public static final int WIDTH = 429; //窗口的宽
    public static final int HEIGHT = 570; //窗口的高
    public static final int ROWS = 8; //8行
    public static final int COLS = 6; //6列
    public static final int ELEMENT_SIZE = 60; //元素大小
    public static final int OFFSET = 30; //偏移量(第1个元素距窗口左边缘的距离)
    private Element[][] elements = new Element[ROWS][COLS]; //元素数组(8行6列)

    //元素类型
    public static final int ELEMENT_TYPE_BEAR = 0; //熊
    public static final int ELEMENT_TYPE_BIRD = 1; //鸟
    public static final int ELEMENT_TYPE_FOX  = 2; //狐狸
    public static final int ELEMENT_TYPE_FROG = 3; //青蛙

    /** 创建元素(根据行列计算x/y坐标，而后创建对象) */
    public Element createElement(int row,int col){
        int x = OFFSET+col*ELEMENT_SIZE; //列col的值控制x坐标
        int y = OFFSET+row*ELEMENT_SIZE; //行row的值控制y坐标
        Random rand = new Random(); //随机数对象
        int type = rand.nextInt(4); //0到3之间
        switch(type){ //根据type值的不同来生成不同的对象
            case ELEMENT_TYPE_BEAR:
                return new Bear(x,y);
            case  ELEMENT_TYPE_BIRD:
                return new Bird(x,y);
            case ELEMENT_TYPE_FOX:
                return new Fox(x,y);
            default:
                return new Frog(x,y);
        }
    }

    //元素可消状态
    public static final int ELIMINATE_NON = 0; //不可消
    public static final int ELIMINATE_ROW = 1; //行可消
    public static final int ELIMINATE_COL = 2; //列可消

    /** 检测元素是否是可消的 返回元素可消状态(0/1/2) */
    public int canEliminate(int row,int col){
        Element e = elements[row][col]; //获取当前元素
        //判断横向
        if(col>=2){
            Element e1 = elements[row][col-1]; //获取当前元素前面第1个元素
            Element e2 = elements[row][col-2]; //获取当前元素前面第2个元素
            if(e!=null && e1!=null && e2!=null){ //若元素都不是null
                //若当前元素与它前面的两个元素类型都相同
                if(e.getClass().equals(e1.getClass()) && e.getClass().equals(e2.getClass())){
                    return ELIMINATE_ROW; //返回1，表示行可消
                }
            }
        }
        //判断纵向
        if(row>=2){
            Element e1 = elements[row-1][col]; //获取当前元素上面的第1个元素(1,0)
            Element e2 = elements[row-2][col]; //获取当前元素上面的第2个元素(0,0)
            if(e!=null && e1!=null && e2!=null){ //若元素都不是null
                //若当前元素与它上面的两个元素的类型相同
                if(e.getClass().equals(e1.getClass()) && e.getClass().equals(e2.getClass())){
                    return ELIMINATE_COL; //返回2，表示列可消
                }
            }
        }
        return ELIMINATE_NON; //返回0，表示不可消

    }

    /** 填充所有元素 */
    public void fillAllElements(){
        for(int row=0;row<ROWS;row++){ //遍历所有行
            for(int col=0;col<COLS;col++){ //遍历所有列
                do{
                    Element e = createElement(row,col); //创建元素
                    elements[row][col] = e; //将元素填充到elements数组中
                }while(canEliminate(row,col)!=ELIMINATE_NON); //若可消则重新生成元素
            }
        }
    }

    /** 判断两个元素是否能交换(相邻则能交换，否则不能交换) */
    public boolean isAdjacent(){
        //若行相邻并且列相等，或，若列相邻并且行相等
        if((Math.abs(firstRow-secondRow)==1 && firstCol==secondCol) || (Math.abs(firstCol-secondCol)==1 && firstRow==secondRow)){
            return true; //相邻
        }else{
            return false; //不相邻
        }
    }

    /** 移动两个元素(动画移动) */
    public void moveElement(){
        if(firstRow==secondRow){ //若行号相同，表示左右动
            int firstX = OFFSET+firstCol*ELEMENT_SIZE; //第1个元素的x坐标
            int secondX = OFFSET+secondCol*ELEMENT_SIZE; //第2个元素的x坐标
            int step = firstX<secondX?4:-4; //步长(控制左右)，值越大速度越快
            for(int i=0;i<15;i++){ //走15次(每次走4，共15*4为60)
                try{ //每动一下休眠10毫秒
                    Thread.sleep(10);
                }catch (InterruptedException e){
                    e.printStackTrace();
                }
                firstX+=step;  //firstX加
                secondX-=step; //secondX减
                //修改元素坐标(刚刚只是在修改变量的值，一定是元素坐标修改之后才能看到移动)
                elements[firstRow][firstCol].setX(firstX);
                elements[secondRow][secondCol].setX(secondX);
                repaint(); //重画
            }
        }
        if(firstCol==secondCol){ //若列号相同，表示上下动
            int firstY = OFFSET+firstRow*ELEMENT_SIZE; //第一个元素的y坐标
            int secondY = OFFSET+secondRow*ELEMENT_SIZE; //第二个元素的y坐标
            int step = firstY<secondY?4:-4; //步长(控制上下)，值越大速度越快
            for(int i=0;i<15;i++){ //以4为步长移动15次，正好移动了60
                try{ //每动一下休眠10毫秒
                    Thread.sleep(10);
                }catch (InterruptedException e){
                    e.printStackTrace();
                }
                firstY+=step; //firstY加
                secondY-=step; //secondY减
                elements[firstRow][firstCol].setY(firstY);    //修改第1个元素的y坐标
                elements[secondRow][secondCol].setY(secondY); //修改第2个元素的y坐标
                repaint();
            }
        }
    }

    /** 交换两个元素 */
    public void exchangeElement(){
        Element e1 = elements[firstRow][firstCol];   //获取第1个选中元素
        Element e2 = elements[secondRow][secondCol]; //获取第2个选中元素
        elements[firstRow][firstCol] = e2;   //将第1个选中元素修改为第2个元素
        elements[secondRow][secondCol] = e1; //将第2个选中元素修改为第1个元素
    }

    /** 扫描并删除元素 */
    public boolean eliminateElement(){
        boolean havaEliminated = false; //是否有元素被消除了
        for(int row=ROWS-1;row>=0;row--){ //扫描所有行(7/6/5/4/3/2/1/0)
            for(int col=COLS-1;col>=0;col--){ //扫描所有列(5/4/3/2/1/0)
                Element e = elements[row][col]; //获取当前元素
                if(e==null){ //若元素为null，则跳过
                    continue;
                }
                //1)查找一行中连续的个数，查找一列是连续的个数
                //2)将连续(可消除)的元素设计为可消除状态
                //3)将可消状态的元素绘制爆炸动画
                //4)将可消状态元素设置为null，以等待其它元素的下落

                //1)查找一行中当前元素前面的连续的个数，查找一列中当前元素前面的连续的个数
                int colRepeat = 0; //行不变，列相邻--与当前元素相邻的行元素的连续重复个数
                for(int pc=col-1;pc>=0;pc--){ //倒着遍历当前元素前面的所有列
                    if(elements[row][pc]==null){ //若前面的元素为null，则直接退出(不用再找了)
                        break;
                    }
                    //若遍历元素与当前元素的类型相同，则重复个数增1，否则break退出
                    if(elements[row][pc].getClass()==e.getClass()){
                        colRepeat++;
                    }else{
                        break; //只要有一个不同的，剩余的不再需要比较了
                    }
                }
                int rowRepeat = 0; //列不变，行相邻--与当前元素相邻的列元素的连续重复个数
                for(int pr=row-1;pr>=0;pr--){ //倒着遍历当前元素上面的所有行
                    if(elements[pr][col]==null){ //若上面的元素为null，则退出(后面的不比了)
                        break;
                    }
                    //若遍历元素与当前元素的类型相同，则重复个数增1，否则break退出(后面的不比了)
                    if(elements[pr][col].getClass()==e.getClass()){
                        rowRepeat++;
                    }else{
                        break;
                    }
                }

                //2)将连续(可消除)的元素设计为可消除状态
                if(colRepeat>=2){ //行不变，列相邻--若同一行相邻元素个数>=2，表示可消
                    elements[row][col].setEliminated(true); //设置当前元素可消
                    for(int i=1;i<=colRepeat;i++){ //遍历连续个数次
                        elements[row][col-i].setEliminated(true); //行不变，列前元素设置为可消
                    }
                }
                if(rowRepeat>=2){ //列不变，行相邻--若同一列相邻元素个数>=2，表示可消
                    elements[row][col].setEliminated(true); //设置当前元素可消
                    for(int i=1;i<=rowRepeat;i++){ //遍历连续个数次
                        elements[row-i][col].setEliminated(true); //列不变，行上元素设置为可消
                    }
                }

                //3)将可消状态的元素绘制爆炸动画
                if(colRepeat>=2 || rowRepeat>=2){ //如果有可消除元素
                    for(int i=0;i<Images.bombs.length;i++){ //遍历所有爆破图
                        repaint(); //重画，依次显示4张爆破图
                        try{ //每爆一下休眠50毫秒
                            Thread.sleep(50);
                        }catch (InterruptedException ex){
                            ex.printStackTrace();
                        }
                    }
                }

                //4)将可消状态元素设置为null，以等待其它元素的下落
                if(colRepeat>=2){ //行不变，列相邻
                    elements[row][col] = null; //将当前元素设置为null
                    for(int i=1;i<=colRepeat;i++){ //遍历连续个数次
                        elements[row][col-i] = null; //行不变，列前元素设置为null
                    }
                    havaEliminated = true; //有可消元素被消除了
                }
                if(rowRepeat>=2){ //列不变，行相邻
                    elements[row][col] = null; //当前元素设置为null
                    for(int i=1;i<=rowRepeat;i++){ //遍历连续个数次
                        elements[row-i][col] = null; //列不变，行上元素设置为null
                    }
                    havaEliminated = true; //有可消元素被消除了
                }

            }
        }
        return havaEliminated;
    }

    /** 下落元素 */
    public void dropElement(){
        for(int row=ROWS-1;row>=0;row--){ //遍历所有行(从下往上遍历)
            //只要有null元素就要将它上面的元素全都落下来，当没有null元素了则结束当前行操作
            while(true){ //自造死循环
                int[] nullCols = {}; //当前行为null的列号
                for(int col=COLS-1;col>=0;col--){ //遍历所有列
                    Element e = elements[row][col]; //获取当前元素
                    if(e==null){ //若当前列元素为null
                        nullCols = Arrays.copyOf(nullCols,nullCols.length+1); //扩容
                        nullCols[nullCols.length-1] = col; //将null元素的列号填充到最后一个元素上
                    }
                }

                if(nullCols.length>0){ //当前行有为null的列
                    //以4为步长，移动15次，正好移好(大小正好是60)
                    for(int count=0;count<15;count++){ //播放15帧动画
                        for(int i=0;i<nullCols.length;i++){ //所有null元素都要落
                            int nc = nullCols[i]; //获取null的列号
                            for(int dr=row-1;dr>=0;dr--){ //从当前行的上一行开始下落元素
                                Element e = elements[dr][nc]; //获取上方的元素
                                if(e!=null){ //若元素不是null
                                    e.setY(e.getY()+4); //设置元素y坐标为--本身y坐标+步长4
                                }
                            }
                        }
                        try{ //每动一下休眠10毫秒
                            Thread.sleep(10);
                        }catch (InterruptedException e){
                            e.printStackTrace();
                        }
                        repaint(); //重画
                    }

                    //真正让数组上面的元素往下移动
                    for(int i=0;i<nullCols.length;i++){ //遍历所有null列号
                        int nc = nullCols[i]; //获取当前null列号
                        //从当前行开始，一直到最上面(不包括0，因为第1行要生成新元素)，元素依次修改
                        for(int nr=row;nr>0;nr--){
                            elements[nr][nc] = elements[nr-1][nc]; //将当前元素赋值为它上一行的元素
                        }
                        elements[0][nc] = createElement(0,nc); //最上面生成新元素
                    }
                    /*
                      带数:假设6,2为null，意味着row=6,nc=2
                        nr=6  62=52
                        nr=5  52=42
                        nr=4  42=32
                        nr=3  32=22
                        nr=2  22=12
                        nr=1  12=02
                              02=新元素

                     */
                }else{ //当前行没有null列，则break退出当前行处理，进行开始处理上一行
                    break;
                }
            }
        }
    }

    private boolean canInteractive = true; //可交互状态(默认可交互)
    private int selectedNumber = 0; //已经选中的元素个数
    private int firstRow = 0;  //第一个选中元素的ROW
    private int firstCol = 0;  //第一个选中元素的COL
    private int secondRow = 0; //第二个选中元素的ROW
    private int secondCol = 0; //第二个选中元素的COL
    /** 启动程序执行 */
    public void start(){
        fillAllElements(); //填充所有元素

        MouseAdapter adapter = new MouseAdapter() {
            /** 重写鼠标点击事件 */
            public void mouseClicked(MouseEvent e) {
                if(!canInteractive){ //不可交互时，不响应鼠标事件
                    return;
                }
                canInteractive = false; //只要选中元素，就先将状态修改为不可交互
                int row = (e.getY()-OFFSET)/ELEMENT_SIZE; //获取选中元素的ROW
                int col = (e.getX()-OFFSET)/ELEMENT_SIZE; //获取选中元素的COL
                selectedNumber++; //选中元素个数增1
                if(selectedNumber==1){ //第1次选中
                    firstRow = row; //记录第1个元素的ROW
                    firstCol = col; //记录第1个元素的COL
                    elements[firstRow][firstCol].setSelected(true); //设置选中状态
                    canInteractive = true; //可交互
                }else if(selectedNumber==2){ //第2次选中
                    secondRow = row; //记录第2个元素的ROW
                    secondCol = col; //记录第2个元素的COL
                    elements[secondRow][secondCol].setSelected(true); //设置选中状态
                    if(isAdjacent()){ //若相邻
                        new Thread(){
                            public void run(){ //自动执行
                                elements[firstRow][firstCol].setSelected(false); //取消选中状态
                                elements[secondRow][secondCol].setSelected(false); //取消选中状态
                                moveElement(); //移动两个元素(动画移动)
                                exchangeElement(); //交换两个元素
                                if(eliminateElement()){ //若有可消并消除了
                                    do{
                                        dropElement();//下落元素
                                        try{ //每动一下休眠10毫秒
                                            Thread.sleep(10);
                                        }catch (InterruptedException e){
                                            e.printStackTrace();
                                        }
                                    }while(eliminateElement()); //扫描有可消元素就继续消并下落，没有可消则结束
                                }else{ //没有可消，需要换回去
                                    moveElement(); //移动两个元素(动画移动)
                                    exchangeElement(); //交换两个元素
                                }
                                canInteractive = true; //可交互
                            }
                        }.start(); //启动线程
                    }else{ //若不相邻
                        elements[firstRow][firstCol].setSelected(false);   //取消选中状态
                        elements[secondRow][secondCol].setSelected(false); //取消选中状态
                        canInteractive = true; //可交互
                    }
                    selectedNumber = 0; //选中个数归0
                }
                repaint(); //重画(调用paint()画)
            }
        }; //鼠标侦听器
        this.addMouseListener(adapter); //添加鼠标侦听
    }

    /** 重写paint()画  g:画笔 */
    public void paint(Graphics g){
        Images.background.paintIcon(null,g,0,0); //画背景图
        for(int row=0;row<ROWS;row++){ //遍历所有行
            for(int col=0;col<COLS;col++){ //遍历所有列
                Element e = elements[row][col]; //根据行列获取元素
                if(e!=null){ //若元素不是null
                    e.paintElement(g); //画元素
                }
            }
        }
    }

    public static void main(String[] args) {
        JFrame frame = new JFrame();
        World world = new World();
        world.setFocusable(true);
        frame.add(world);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(WIDTH,HEIGHT+17);
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);

        world.start(); //启动程序的执行
    }
}















